/**
 * $Id: MultiSessionFilter.java 73 2013-03-21 14:48:53Z emasab@gmail.com $
 */
package it.edgeworks.dsession.api;

import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This filter provides a standard distributed multi session.
 *
 * @author Daniele Galdi, Ivan Diana
 * @version $Revision: 73 $
 */
public class MultiSessionFilter implements Filter {

    protected final Log logger = LogFactory.getLog(getClass());

    private static String PROVIDER_CLASS = "provider-class";

    private static String SESSION_ID = "DSESSID";

    private HttpSessionProvider provider;

    private AtomicInteger counter;

    private String cachedPrefix;

    private boolean traceRequest = false;

    public void init(FilterConfig filterConfig) throws ServletException {
        String providerClassName = filterConfig.getInitParameter(PROVIDER_CLASS);

        logger.info("Initializing " + getClass().getSimpleName() + " with provider " + providerClassName);

        if (providerClassName == null) {
            throw new ServletException(getClass().getSimpleName() + " the parameter " + PROVIDER_CLASS + " is null");
        }
        try {
            this.provider = (HttpSessionProvider) Class.forName(providerClassName).newInstance();
        } catch (Exception e) {
            throw new ServletException(getClass().getSimpleName() + " Cannot instanciate the: " + providerClassName);
        }

        this.provider.init(filterConfig);
        this.counter = new AtomicInteger();
    	this.cachedPrefix = UUID.randomUUID().toString();

    	try{
    		if( filterConfig.getInitParameter("trace-request") != null )
    			this.traceRequest = Boolean.parseBoolean(filterConfig.getInitParameter("trace-request"));
    	}catch (Exception e) {
			this.traceRequest = false;
		}
    }

    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {

    	double rand = Math.random();
    	long start = new java.util.Date().getTime();
    	String urlRequested = null;
    	logger.info(rand + " [Richiesta]MultiSessionFilter inizio: " + start);

        if (request instanceof HttpServletRequest) {
        	HttpServletRequest httpRequest = (HttpServletRequest) request;
        	urlRequested = httpRequest.getRequestURI();
        	logger.debug(rand + " [Richiesta]MultiSessionFilter - URL:" + httpRequest.getRequestURI());
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            HttpSession session = httpRequest.getSession();

            String sessionId = checkSession(httpRequest);
            logger.debug("MultiSessionFilter sessionId:" + sessionId);

            boolean validSession = false;
            if (sessionId != null){
            	logger.debug(rand + " [Richiesta]MultiSessionFilter - Start loading session:" + sessionId);
                validSession = provider.loadSession(sessionId, session);
                logger.debug(rand + " [Richiesta]MultiSessionFilter - End loading validSession:" + validSession);
                if (!validSession) {
                    try {
                    	session.invalidate();
                    } catch(IllegalStateException ex){
                    	logger.warn(rand + " [Richiesta]MultiSessionFilter - Session already invalidated:" + sessionId);                 	
                    }
                }
            }

            if (sessionId == null || !validSession){
                sessionId = generatePrefix();	// create new dsession id
                session = httpRequest.getSession(true);	// create new http session
            	Cookie sessionCookie = new Cookie(SESSION_ID, sessionId);	// create new cookie
                httpResponse.addCookie(sessionCookie);	// send ever the cookie
                logger.debug("Resetting sessionCookie before:" + sessionCookie);
            }

            if( this.traceRequest ) session.setAttribute("dsession-requestid", rand);

            chain.doFilter(request, response);

            logger.debug(rand + " [Richiesta]MultiSessionFilter - start saving session");
            provider.saveSession(sessionId, session);
            logger.debug(rand + " [Richiesta]MultiSessionFilter - end saving session");
        } else {
            chain.doFilter(request, response);
        }        	

        long stop = new java.util.Date().getTime();
        long delta = stop - start;
        logger.info(rand + " [Richiesta]MultiSessionFilter fine (elapsed time: " + delta + " millisec) - url:" + urlRequested);
    }

    protected String generatePrefix() {
        return this.cachedPrefix + "-" + counter.getAndIncrement();
    }

    protected String checkSession(HttpServletRequest httpRequest) {
        String sessionId = null;
        Cookie[] cookies = httpRequest.getCookies();
        if ( cookies != null ) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(SESSION_ID)) {
                    sessionId = cookie.getValue();
                    break;
                }
            }
        }
        return sessionId;
    }

    public Cookie getSessionCookie(Cookie[] cookies) {
        if ( cookies != null ) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(SESSION_ID)) {
                    return cookie;
                }
            }
        }
        return null;
    }

    public void destroy() {
        provider.destroy();
    }

}
